/*  Copyright 2003  Auri Marcelo Rizzo Vicenzi, Marcio Eduardo Delamaro, 			    Jose Carlos Maldonado

    This file is part of Jabuti.

    Jabuti is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as 
    published by the Free Software Foundation, either version 3 of the      
    License, or (at your option) any later version.

    Jabuti is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Jabuti.  If not, see <http://www.gnu.org/licenses/>.
*/


package br.jabuti.project;


import java.io.*;

import br.jabuti.lookup.*;

import org.apache.bcel.util.*;
import org.apache.bcel.classfile.*;

import br.jabuti.util.*;
import br.jabuti.project.JabutiProject;


/**
 * This class represents a single class file. 
 * It encloses the original bytecode of this class file and
 * also the instrumented bytecode that is used by our 
 * class loader {@link br.jabuti.instrumenter.LoadExec} to run and 
 * collect dinamic information during the execution.
 * 
 * When avaliable, this class also contains a reference for the
 * original java source code ({@link SourceFile}). 
 *
 * @version: 1.0
 * @author: Auri Vincenzi
 * @author: Marcio Delamaro
 * @author: Tatiana Sugeta
 *
 */
public class ClassSourceFile {
	
    /** The source file name */
    private String sourceName = null;
	
    /** The local source file name: system dependent */
    private String localSourceName = null;

    /** The type of the source file: JavaClass.HEAP, JavaClass.FILE or JavaClass.ZIP */
    private byte type = -1;

    // Used by toString method to ident the output
    String prefix = new String("");
	
    // Variable to deal with class files
    private RClassCode classCode = null;
    private JavaClass javaClass = null;

    /** The class file that this method belongs */
    private ClassFile classFile = null;

	private JabutiProject prj = null;
	
    /** Creates a Class File */
    public ClassSourceFile(JabutiProject p, RClassCode cc) {
    	prj = p;
    	
        classCode = cc;
		
        javaClass = classCode.getTheClass();
		
        /* ERROR: Old version... when the class file is in a different
         directory, i.e., inside the package name directory, 
         the  source file was not found...
         
         sourceName = classCode.getPackageName() + "."
         + javaClass.getSourceFileName();
         
         localSourceName = getLocalSourceFileName();			
         
         */
		
        // Try to find the source name only using the name of the souce file...
        sourceName = javaClass.getSourceFileName();
        
        // Trying to find the local source file
        localSourceName = getLocalSourceFileName();			        
        
        /* The ajc compiler for aspectJ considers 
           that a .class file maybe generated by more than one
           .java file and the javaClass.getSourceFileName() returns
           the list of .java files separeted by semicolon (;)
           
           We found a soluction that is to preprocess the aspectJ source 
           code and then compile it using the traditional javac compiler
           to avoide such a problem. Moreover, the ajc does not establish
           the correct correspondence between source line and bytecode offset.
        
        int i = 0;
        if ( (i = sourceName.indexOf( ";" ) ) > 0 )
        	sourceName = sourceName.substring( 0, i );
        */
        //localSourceName = getLocalSourceFileName();
    }

    /***********************************************************/
    
    /* Get and Set Methods implementation                      */
    
    /***********************************************************/
    public String getSourceName() {
        return sourceName;
    }
	
   /* private void setSourceName(String n) {
        sourceName = n;
    }*/

    public String getLocalSourceName() {
        return localSourceName;
    }
	
    /*private void setLocalSourceName(String n) {
        localSourceName = n;
    }*/

    public byte getType() {
        return type;
    }

    public BufferedReader getSourceCode() {
        if (exists()) {
            try {
                BufferedReader buf = new BufferedReader(new FileReader(getLocalSourceName()));
	
                return buf;
            } catch (IOException ioe) {
                ToolConstants.reportException(ioe, ToolConstants.STDERR);
                return null;
            }
        } else
            return null;
    }

    /**
     * This method checks if the source file of a given class file
     * is available. If it is available returns the complete path for the
     * source file, if not returns null.
     *
     * @ return the complete path to the source file of a given class file or
     * null in case it does not exist.
     */
    public String getLocalSourceFileName() {
        String sourceName = getSourceName();

		localSourceName = findLocalSourceFileName( sourceName );
        
        // If not found, add the name of the package to the file...
        if (!exists()) {
            if (classCode.getPackageName().length() > 0) {
                sourceName = classCode.getPackageName() + File.separator
                        + javaClass.getSourceFileName();
	            
                localSourceName = findLocalSourceFileName( sourceName );
            }
        }
        return localSourceName;
    }
    
    private String findLocalSourceFileName( String sourceName ) {
        //System.out.println("THE SOURCE NAME: " + sourceName);
		
		ClassPath cp = null;
		
		if ( prj != null ) { // If the project is activated, include additional paths
        	cp = new ClassPath(System.getProperty("java.class.path") + 
        							File.pathSeparator + prj.getClasspath() );
        }
        else {
        	cp = new ClassPath(System.getProperty("java.class.path") );
        }
        	
        String sourcePath = null;
		
        try {
        	/*StringTokenizer st = new StringTokenizer( System.getProperty("java.class.path"),
        		File.pathSeparator );
     	    while (st.hasMoreTokens()) {
         		System.out.println(st.nextToken());
     		}*/
         		
            sourcePath = cp.getPath(sourceName);
        } catch (IOException io) {
            return null;
        }

        File file = new File(sourcePath);
        if (file.exists()) { 
          	//System.out.println( "SOURCE FOUND AT: " + sourcePath );
            return sourcePath;
        } else { // Source not found
            return null;
        }
    }

    public ClassFile getClassFile() {
        return classFile;
    }
	
    /*private void setClassFile(ClassFile cf) {
        classFile = cf;
    }*/

    public boolean exists() {
//    	if ( localSourceName == null )
//    		localSourceName = getLocalSourceFileName();
        return (localSourceName != null);
    }
	
    public String toString(String p) {
        prefix = p;
        return toString();
    }
	
    public String toString() {
        String out = new String(prefix + "Source File: " + getSourceName() + "\n");
		
        out = out + prefix + "\tType: " + getType() + "\n";
		
        String tmp;
		
        if (exists()) {
            tmp = getLocalSourceName();
        } else {
            tmp = new String("Source file not found!!!");
        }
			
        out = out + prefix + "\tLocation: " + tmp + "\n";

        return out;
    }
}
